﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Couchbase;
using Couchbase.Core;
using Couchbase.N1QL;
using Couchbase.Views;

using SharpSoda.Message;

namespace SharpSoda.Storage
{
    public class GlobalStorage : IStateStorage
    {
        private string m_StorageName = "";
        private bool m_HasExpiration = false;
        private TimeSpan m_Expiration = TimeSpan.MaxValue;

        public GlobalStorage(string storageName)
        {
            m_StorageName = storageName;
            m_HasExpiration = false;
            m_Expiration = TimeSpan.MaxValue;
        }

        public GlobalStorage(string storageName, TimeSpan expiration)
        {
            m_StorageName = storageName;
            m_HasExpiration = true;
            m_Expiration = expiration;
        }

        public virtual ulong GetState<T>(string objectName, out T state, ulong flag = 0) where T : class
        {
            state = null;
            ulong ret = 0;
            if (objectName == null) return ret;

            try
            {
                IBucket bucket = DataAccessHelper.GetNoSqlDataSet(m_StorageName);
                if (bucket == null) return ret;

                if (flag == 0)
                {
                    IOperationResult<T> result = null;
                    if (!m_HasExpiration) result = bucket.Get<T>(objectName);
                    else result = bucket.GetAndTouch<T>(objectName, m_Expiration);
                    if (result != null && result.Success) state = result.Value;
                }
                else
                {
                    bool existing = true;
                    if (m_HasExpiration) existing = bucket.Touch(objectName, m_Expiration).Success;

                    if (existing)
                    {
                        var result = bucket.GetWithLock<T>(objectName, TimeSpan.FromSeconds(20));
                        if (result.Success)
                        {
                            ret = result.Cas;
                            state = result.Value;
                        }
                    }
                }
            }
            catch { }

            return ret;
        }

        public virtual List<T> GetStates<T>(params object[] filters) where T : class
        {
            List<T> list = new List<T>();
            if (filters != null && filters.Length > 0)
            {
                if (filters.Length == 1 && filters[0] != null)
                {
                    IQueryRequest query = null;
                    if (filters[0] is string) query = new QueryRequest().Statement(filters[0] as string);
                    else if (filters[0] is QueryRequest) query = filters[0] as QueryRequest;

                    if (query != null)
                    {
                        IBucket bucket = DataAccessHelper.GetNoSqlDataSet(m_StorageName);
                        if (bucket != null)
                        {
                            var result = bucket.Query<dynamic>(query);
                            if (result.Success)
                            {
                                foreach (var row in result.Rows)
                                    list.Add(JsonHelper.FromString<T>(row[m_StorageName].ToString()));
                            }
                        }
                    }
                }
                else if (filters.Length >= 2 && filters[0] is string && filters[1] is string)
                {
                    string docName = filters[0] as string;
                    string viewName = filters[1] as string;
                    if (docName != null && viewName != null)
                    {
                        IBucket bucket = DataAccessHelper.GetNoSqlDataSet(m_StorageName);
                        if (bucket != null)
                        {
                            IViewQuery viewQuery = null;
                            if (filters.Length == 2) viewQuery = bucket.CreateQuery(docName, viewName);
                            else if (filters.Length == 3) viewQuery = bucket.CreateQuery(docName, viewName).Key(filters[2]);
                            else if (filters.Length == 4) viewQuery = bucket.CreateQuery(docName, viewName).StartKey(filters[2]).EndKey(filters[3]);
                            else viewQuery = bucket.CreateQuery(docName, viewName).StartKey(filters[2]).EndKey(filters[3]).Limit(Convert.ToInt32(filters[4].ToString()));
                            if (viewQuery != null)
                            {
                                var resultView = bucket.Query<dynamic>(viewQuery);
                                if (resultView.Success)
                                {
                                    foreach (var item in resultView.Rows)
                                        list.Add(JsonHelper.FromString<T>(item.Value.ToString()));
                                }
                            }
                        }
                    }
                }
            }
            return list;
        }

        public virtual ulong SetState<T>(string objectName, T state, ulong flag = 0) where T : class
        {
            if (objectName == null || state == null) return 1;
            if (m_StorageName == null || m_StorageName.Length <= 0) return 1;

            try
            {
                IBucket bucket = DataAccessHelper.GetNoSqlDataSet(m_StorageName);
                if (bucket == null) return 1;
                IOperationResult<T> result = null;
                if (flag > 0) bucket.Unlock(objectName, flag);
                if (m_HasExpiration) result = bucket.Upsert<T>(objectName, state, m_Expiration);
                else result = bucket.Upsert<T>(objectName, state);
                if (result.Success) return 0;
            }
            catch { }

            return 1;
        }

        public virtual T RemoveState<T>(string objectName) where T : class
        {
            T state = null;

            if (objectName == null) return state;

            try
            {
                IBucket bucket = DataAccessHelper.GetNoSqlDataSet(m_StorageName);
                if (bucket == null) return state;
                var result = bucket.GetWithLock<T>(objectName, TimeSpan.FromSeconds(20));
                if (result.Success)
                {
                    state = result.Value;
                    bucket.Remove(objectName, result.Cas);
                }
            }
            catch { }

            return state;
        }

    }
}
